package com.atguigu.gmall.item.service.impl;

import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.feign.product.SkuDetailFeignClient;
import com.atguigu.gmall.feign.search.SearchFeignClient;
import com.atguigu.gmall.item.service.CacheOpsService;
import com.atguigu.gmall.item.service.SkuDetailService;
import com.atguigu.gmall.product.entity.SkuImage;
import com.atguigu.gmall.product.entity.SkuInfo;
import com.atguigu.gmall.product.entity.SpuSaleAttr;
import com.atguigu.gmall.web.SkuDetailVo;
import com.atguigu.gmall.web.SkuDetailVo.CategoryView;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnel;
import com.google.common.hash.Funnels;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author:ahang
 * @create:2022-10-04 18:59
 * @Description:
 */
@Slf4j
@Service
public class SkuDetailServiceImpl implements SkuDetailService {

    @Autowired
    SkuDetailFeignClient skuDetailFeignClient;

    @Autowired
    StringRedisTemplate redisTemplate;


    @Autowired
    ThreadPoolExecutor poolExecutor;

    @Autowired
    RedissonClient redissonClient;

    BloomFilter<Long> filter = null;


    /**
     * 组件创建好之后执行此方法
     */
    @PostConstruct
    void init(){
        Funnel<Long> funnel = Funnels.longFunnel();
        //创建一个布隆过滤器
        filter = BloomFilter.create(funnel, 1000000, 0.0000001);
        //获取到所有的skuId
        Result<List<Long>> allSkuId = skuDetailFeignClient.getAllSkuId();
        allSkuId.getData().forEach(item -> {
            filter.put(item);
        });
    }

    @Autowired
    CacheOpsService cacheOpsService;

    /**
     * 显示详情页
     * @param skuId
     * @return
     */
    @Override
    public SkuDetailVo skuDetail(Long skuId) {
        String key = RedisConst.SKU_DETAIL_CACHE_PREFIX +skuId;
        //1.查缓存
        SkuDetailVo data = cacheOpsService.getCacheData(key,SkuDetailVo.class);
        //2.判断缓存中是否存在 --如果缓存中查到占位符,直接有自动异常处理返回异常
        if(data != null){
            //3.缓存中查到
            return data;
        }
        //4.缓存中真没有,需要回源（要解决穿透和击穿问题）
        boolean contain = filter.mightContain(skuId);
        if(!contain){
            //布隆说没有就是真没有
            return null;
        }
        //5.开始回源,解决击穿
        //6.准备一个分布式锁.针对不同商品设置不同的细粒度锁
        RLock lock = redissonClient.getLock(RedisConst.LOCK_PREFIX + skuId);
        //这里不能用lock阻塞式锁
        boolean b = lock.tryLock();
        if(b){
            try {
                //8.拿到锁
                //这里引入双检查机制,防止抢锁卡顿问题:一个线程已经抢完锁查完数据回源了,
                // 另一个才发送完抢锁命令,造成再去数据库中查询
                SkuDetailVo cacheData = cacheOpsService.getCacheData(key, SkuDetailVo.class);
                if(cacheData == null){
                    //10.回源
                    SkuDetailVo skuDetailFromRpc = getSkuDetailFromRpc(skuId);
                    cacheData = skuDetailFromRpc;
                    //11.放入缓存
                    cacheOpsService.setDate(key,skuDetailFromRpc,7L, TimeUnit.DAYS);
                }
                return cacheData;
            }finally {
                lock.unlock();
            }
        }else{
            //9.没拿到锁
            try {
                Thread.sleep(500);
                //重新查缓存,递归查询,很容易栈溢出
//                return skuDetail(skuId);
                return cacheOpsService.getCacheData(key,SkuDetailVo.class);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }



//    @Override
//    public SkuDetailVo skuDetail(Long skuId) {
//
////        //1.布隆判断没有直接返回null
//        if(!filter.mightContain(skuId)){
//            log.info("无此商品");
//            return null;
//        }
//        //2.判断redis中有没有
//        String json = redisTemplate.opsForValue().get("sku:info:" + skuId);
//            //3.如果有,将redis中的字符串转换成对象返回
//        if(json != null){
//            SkuDetailVo skuDetailVo = Jsons.toObject(json, SkuDetailVo.class);
//            return skuDetailVo;
//        }
//        //4.如果没有,回源查询数据,并且将数据保存在redis中,并且判断查询的数据是否为空
//        SkuDetailVo skuDetailFromRpc = this.getSkuDetailFromRpc(skuId);
//        String data = "";
//        if(StringUtils.isEmpty(skuDetailFromRpc)){
//            data = "*";
//        }else{
//            data = Jsons.tostr(skuDetailFromRpc);
//        }
//        redisTemplate.opsForValue().set("sku:info:" + skuId,data);
//        //5.返回数据
//        return skuDetailFromRpc;
//
//    }

    @Autowired
    SearchFeignClient searchFeignClient;
    /**
     * 增加热度分
     * @param skuId
     */
    @Override
    public void incrHotSccore(Long skuId) {
        Long increment = redisTemplate.opsForValue().increment("hotscore" + skuId);
        if(increment%100 == 0){
            //调用es远程接口,修改热度分
            searchFeignClient.updateHotScore(skuId,increment);
        }
    }


    /**
     * 抽取的详情页
     * @param skuId
     * @return
     */
    private SkuDetailVo getSkuDetailFromRpc(Long skuId) {
        SkuDetailVo data = new SkuDetailVo();

        CompletableFuture<SkuInfo> skuInfoFuture = CompletableFuture.supplyAsync(() -> {
            //1、查询出sku_info信息
            Result<SkuInfo> skuInfo = skuDetailFeignClient.getSkuInfo(skuId);
            SkuInfo info = skuInfo.getData();
            //设置好skuinfo的数据返回
            data.setSkuInfo(info);
            return info;
        },poolExecutor);

        //        SkuDetailVo skuDetailVo = new SkuDetailVo();
//        //1.查询skuinfo详情
//        Result<SkuInfo> data = skuDetailFeignClient.getSkuInfo(skuId);
//        //1.2查询skuinfo中的skuImageList
//        Result<List<SkuImage>> images = skuDetailFeignClient.getImage(skuId);
//        List<SkuImage> imageList = images.getData();
////        SkuInfo skuInfo = data.getData();
////        skuInfo.setSkuImageList(imageList);
////        skuDetailVo.setSkuInfo(skuInfo);
////


        //2、查询当前sku的图片列表
        CompletableFuture<Void> imageFuture = skuInfoFuture.thenAcceptAsync(sku -> {
            Result<List<SkuImage>> skuImages = skuDetailFeignClient.getImage(skuId);
            List<SkuImage> images = skuImages.getData();
            sku.setSkuImageList(images);
        },poolExecutor);

//        //查询CategoryView数据
//        Result<CategoryView> category = skuDetailFeignClient.getCategory(skuId);
//        skuDetailVo.setCategoryView(category.getData());
//
        //3、查询sku的分类信息
        CompletableFuture<Void> categoryFuture = skuInfoFuture.thenAcceptAsync((sku) -> {
            Result<CategoryView> categoryView = skuDetailFeignClient.getCategory(sku.getId());
            data.setCategoryView(categoryView.getData());
        },poolExecutor);

        //        //查询实时价格
//        skuDetailVo.setPrice(skuDetailFeignClient.getPrice(skuId).getData());

        CompletableFuture<Void> priceFuture = CompletableFuture.runAsync(() -> {
            //4、查询sku的价格信息。为了得到最新价格，每次都再查一遍
            Result<BigDecimal> skuPrice = skuDetailFeignClient.getPrice(skuId);
            data.setPrice(skuPrice.getData());
        },poolExecutor);

//
//        //查询销售属性列表
//        Long spuId = skuInfo.getSpuId();
//        skuDetailVo.setSpuSaleAttrList(skuDetailFeignClient.getSpuSaleAttrListAndMark(spuId,skuId).getData());
//
        //5、查询sku的销售属性
        CompletableFuture<Void> saleAttrFuture = skuInfoFuture.thenAcceptAsync(res -> {
            Long spuId = res.getSpuId();
            Result<List<SpuSaleAttr>> value = skuDetailFeignClient.getSpuSaleAttrListAndMark(spuId, skuId);
            data.setSpuSaleAttrList(value.getData());
        },poolExecutor);

//
//        skuDetailVo.setValueSkuJson(skuDetailFeignClient.getSpuValueSkuJson(spuId).getData());
//
//

        //6、json 就是 map（"119|120":49,"122:123":50）、javaBean
        CompletableFuture<Void> valueJsonFuture = skuInfoFuture.thenAcceptAsync(res -> {
            Result<String> skuJson = skuDetailFeignClient.getSpuValueSkuJson(res.getSpuId());
            data.setValueSkuJson(skuJson.getData());
        },poolExecutor);

        //以上任务全部运行结束
        CompletableFuture.allOf(imageFuture,categoryFuture,priceFuture,saleAttrFuture,valueJsonFuture).join();

        return data;


//        return skuDetailVo;
    }
}
